FPGA Board Based FPGA for Beginners FPGA Tutor Pocket Boards PRA006/PRA010

Use Dual-port RAM to Read and Write Frame Data, use synchronous (or asynchronous ) clock to control the synchronization of frame structure – FPGA Board Beginner Tutorial – Experiment 9

Experiment 9 Use Dual_port RAM to Read and Write Frame Data

9.1 Experiment Objective

  1. Learn to configure and use dual-port RAM
  2. Learn to use synchronous clock to control the synchronization of frame structure
  3. Learn to use asynchronous clock to control the synchronization of frame structure

Experiment Implement

  1. Observing the synchronization structure of synchronous clock frames using SignalTap II
  2. Extended the use of dual-port RAM
  3. Design the use of three-stage state machine
  4. Design a 16-bit data frame
    1. Data is generated by an 8-bit counter: Data={~counta,counta}
    2. The ID of the data frame inputted by the switch (7 bits express maximum of 128 different data frames)
    3. 16-bit checksum provides data verification
      1. 16-bit checksum accumulates, discarding the carry bit
      2. After the checksum is complemented, append to the frame data
    4. Provide configurable data length data_len by parameter
    5. Packet: When the data and checksum package are written to the dual-port RAM, the userID, the frame length and the valid flag are written to the specific location of the dual-port RAM. The structure of the memory is shown in Table 9.1.
Wr_addr Data/Flag Rd_addr
8’hff {valid,ID,data_len} 8’hff
N/A
8’hnn+2 N/A 8’hnn+2
8’hnn+1 ~checksum+1 8’hnn+1
8’hnn datann 8’hnn
….
8’h01 Data1 8’h01
8’h00 Data0 8’h00

Table 1 Memory Structure

6. Read and write in an agreed order

Valid is the handshake signal. This flag provides the possibility of read and write synchronization, so the accuracy of this signal must be ensured in the program design.

 

 

9.3 Experiment

9.3.1 Introduction of the Program

The first step: the establishment of the main program framework

1
2
3
4
5
6
7
8
9
10
module frame_ram
#(parameter data_len=250)
(
input inclk,
input rst,
input [6:0] sw,
output reg [6:0] oID,
output reg rd_done,
output reg rd_err
);

The second step: the definition of the state machine

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
parameter [2:0] mema_idle=0,
mema_init=1,
mema_pipe0=2,
mema_read0=3,
mema_read1=4,
mema_wr_data=5,
mema_wr_chsum=6,
mema_wr_done=7;
parameter [2:0] memb_idle=0,
memb_init=1,
memb_pipe0=2,
memb_read0=3,
memb_read1=4,
memb_rd_data=5,
memb_rd_chsum=6,
memb_rd_done=7;

The third step: other definitions

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
Clock variable definition
 
wire sys_clk;
 
wire BCD_clk;
 
wire sys_rst;
 
reg ext_clk;
 
Dual-port RAM interface definition
 
reg [7:0] addr_a;
 
reg [15:0] data_a;
 
reg wren_a;
 
wire [15:0] q_a;
 
reg [7:0] addr_b;
 
reg wren_b;
 
wire [15:0] q_b;
 
Write state machine part variable definition
 
reg [6:0] user_id;
 
reg [7:0] wr_len;
 
reg [15:0] wr_chsum;
 
wire wr_done;
 
reg [7:0] counta;
 
wire [7:0] countb;
 
assign countb=~counta;
 
reg [15:0] rd_chsum;
 
reg [7:0] rd_len;
 
reg [15:0] rd_data;
 
reg ext_rst;
 
reg [2:0] sta;
 
reg [2:0] sta_nxt,;
 
Read state machine part variable definition
 
reg [15:0] rd_chsum;
 
reg [7:0] rd_len;
 
reg [15:0] rd_data;
 
reg [2:0] stb;
 
reg [2:0] stb_nxt;

The fourth step: generate dual-port RAM, PLL

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
dp_ram dp_ram_inst
 
(
 
.address_a (addr_a),
 
.address_b (addr_b),
 
.clock (sys_clk),
 
.data_a (data_a),
 
.data_b (16'b0),
 
.wren_a (wren_a),
 
.wren_b (wren_b),
 
.q_a (q_a),
 
.q_b (q_b)
 
);
 
pll_sys_rst pll_sys_rst_inst
 
(
 
.inclk (inclk),
 
.sys_clk (sys_clk),
 
.BCD_clk (BCD_clk),
 
.sys_rst (sys_rst)
 
);

The RAM is 16 bits wide and 256 depth. The PLL inputs a 50 MHz clock, outputs 100 MHz as the operating clock of other modules, and 20 MHz is used to drive the segment display.

The fifth step: data generation counter

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
always @ (posedge sys_clk)
 
if(sys_rst) begin
 
counta <= 0;
 
user_id <= 0;
 
end
 
else begin
 
counta <=counta + 1;
 
user_id <= sw;
 
end

The sixth step: write state machine

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
assign wr_done = (wr_len == (data_len - 1'b1));
 
//Think why using wr_len==data_len-1, instaed of wr_len==data_len
 
//First stage
 
always @ (posedge sys_clk)
 
begin
 
if (sys_rst) begin
 
sta = mema_idle;
 
end
 
else
 
sta = sta_nxt;
 
end
 
//Second stage
 
always @ (*)
 
begin
 
case (sta)
 
mema_idle : sta_nxt = mema_init;
 
mema_init : sta_nxt = mema_pipe0;
 
mema_pipe0 : sta_nxt = mema_read0;
 
mema_read0 :
 
begin
 
if (!q_a[15])
 
sta_nxt = mema_read1;
 
else
 
sta_nxt = sta;
 
end
 
mema_read1 :
 
begin
 
if (!q_a[15])
 
sta_nxt = mema_wr_data;
 
else
 
sta_nxt = sta;
 
end
 
mema_wr_data :
 
begin
 
if (wr_done)
 
sta_nxt = mema_wr_chsum;
 
else
 
sta_nxt = sta;
 
end
 
mema_wr_chsum : sta_nxt = mema_wr_done;
 
mema_wr_done : sta_nxt = mema_init;
 
default : sta_nxt = mema_idle;
 
endcase
 
end  //Third stage
 
always @ (posedge sys_clk)
 
begin
 
case (sta)
 
mema_idle :
 
begin
 
addr_a <= 8'hff;
 
wren_a <= 1'b0;
 
data_a <= 16'b0;
 
wr_len <= 8'b0;
 
wr_chsum <= 0;
 
end
 
mema_init, mema_pipe0, mema_read0, mema_read1 :
 
begin
 
addr_a <= 8'hff;
 
wren_a <= 1'b0;
 
data_a <= 16'b0;
 
wr_len <= 8'b0;
 
wr_chsum <= 0;
 
end
 
mema_wr_data :
 
begin
 
addr_a <= addr_a + 1'b1;
 
wren_a <= 1'b1;
 
data_a <= {countb, counta};
 
wr_len <= wr_len + 1'b1;
 
wr_chsum <= wr_chsum + {countb, counta};
 
end
 
mema_wr_chsum :
 
begin
 
addr_a <= addr_a + 1'b1;
 
wr_len <= wr_len + 1'b1;
 
wren_a <= 1'b1;
 
data_a <= (~wr_chsum) + 1'b1;
 
end
 
mema_wr_done :
 
begin
 
addr_a <= 8'hff;
 
wren_a <= 1'b1;
 
data_a <= {1'b1, user_id, wr_len};
 
end
 
default : ;
 
endcase
 
end

Write order:

  1. Read the flag of the 8’hff address (control word). If valid=1’b0, the program proceeds to the next step, otherwise waits
  2. Address plus 1, 8’hff+1 is exactly zero, write data from 0 address and calculate the checksum
  3. Determine whether the predetermined data length is reached. If so, proceeds to next step, otherwise continue writing data, and the checksum is calculated.
  4. checksum complements and write to memory
  5. Write the control word in the address 8’hff, packet it

The seventh step: read state machine

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
//First stage
 
always @ (posedge sys_clk)
 
begin
 
if (ext_rst) begin
 
stb = memb_idle;
 
end
 
else
 
stb = stb_nxt;
 
end
 
//Second stage
 
always @ (*)
 
begin
 
case (stb)
 
memb_idle : stb_nxt = memb_init;
 
memb_init : stb_nxt = memb_pipe0;
 
memb_pipe0 : stb_nxt = memb_read0;
 
memb_read0 :
 
begin
 
if (q_b[15])
 
stb_nxt = memb_read1;
 
else
 
stb_nxt = memb_init;
 
end
 
memb_read1 :
 
begin
 
if (q_b[15])
 
stb_nxt = memb_rd_data;
 
else
 
stb_nxt = memb_init;
 
end
 
memb_rd_data :
 
begin
 
if(rd_done)
 
stb_nxt = memb_rd_chsum;
 
else
 
stb_nxt = stb;
 
end
 
memb_rd_chsum : stb_nxt = memb_rd_done;
 
memb_rd_done : stb_nxt = memb_init;
 
default : stb_nxt = memb_idle;
 
endcase
 
end
 
// Third stage, the actual operation needs to be driven by the edge of the clock.
 
always @ (posedge sys_clk)
 
begin
 
case (stb)
 
memb_idle :
 
begin
 
addr_b <= 8'hff;
 
rd_data <= 0;
 
rd_chsum <= 0;
 
wren_b <= 1'b0;
 
rd_len <= 8'b0;
 
oID <= 7'b0;
 
rd_err <= 1'b0;
 
end
 
memb_init :
 
begin
 
addr_b <= 8'hff;
 
rd_data <= 0;
 
rd_chsum <= 0;
 
wren_b <= 1'b0;
 
rd_len <= 8'b0;
 
oID <= 7'b0;
 
rd_err <= 1'b0;
 
end
 
memb_pipe0 :
 
begin
 
addr_b <= 8'b0;
 
end
 
memb_read0 :
 
begin
 
if (q_b[15])
 
addr_b <= addr_b + 1'b1;
 
else
 
addr_b <= 8'hff;
 
rd_data <= 0;
 
rd_chsum <= 0;
 
wren_b <= 1'b0;
 
rd_len <= 8'b0;
 
oID <= 7'b0;
 
end
 
memb_read1 :
 
begin
 
if(q_b[15])
 
addr_b <= addr_b + 1'b1;
 
else
 
addr_b <= 8'hff;
 
rd_data <= 0;
 
rd_chsum <= 0;
 
wren_b <= 1'b0;
 
rd_len <= q_b[7:0];
 
oID <= q_b[14:8];
 
end
 
memb_rd_data :
 
begin
 
addr_b <= addr_b + 1'b1;
 
rd_data <= q_b;
 
rd_chsum <= rd_chsum + rd_data;
 
wren_b <= 1'b0;
 
rd_len <= rd_len - 1'b1;
 
end
 
memb_rd_chsum :
 
begin
 
addr_b <= 8'hff;
 
wren_b <= 1'b0;
 
if (|rd_chsum)
 
rd_err <= 1'b1;
 
end
 
memb_rd_done :
 
begin
 
addr_b <= 8'hff;
 
wren_b <= 1'b1;
 
end
 
default : ;
 
endcase
 
end

Read order

  1. Idle is the state after reset
  2. Init: Initialization, set the address to 8’hff
  3. Rd_pipe0: Add a latency (since the read address and data are both latched). Address +1, forming a pipeline structure
  4. Read0: Set the address to 8’hff, read the control word and judge whether the valid bit is valid.
  5. If valid=1’b1, address +1, proceeds to the next step
  6. If valid=1’b0, it means the packet is not ready yet, the address is set to be 8’hff and returns to the init state.
  7. Read1: read the contro word again
  8. If valid=1’b1, address+1, ID and data length are assigned to the corresponding variables and proceeds to the next step
  9. If valid=1’b0, it means the packet is not ready yet, the address is set to 8’hff, and returns to the init state.
  10. Rd_data:
  11. Read data and pass to data variables
  12. Calculate checksum, data_len – 1
  13. Determine whether the data_len is 0
  14. 0: all data has been read, proceeds to the next step
  15. Not 0: continue the operation in current state
  16. rd_chsum: Read the value of checksum and calculate the last checksum. Correct the data and set the flag of rd_err
  17. rd_done: The last step clears the valid flag in memory and opens the write enable for the next packet

Experiment Verification

The first step: pin assignment

See Table 9.2 for the pin assignment.

Table 9.2 Frame data read and write experiment pin mapping

Signal Name Network Label FPGA Pin Port Description
Inclk C10_50MCLK 91 Input clock
rst KEY2 10 Reset signal
sw[6] SW6_LED6 76 7-digit DIP switch

(address input)

sw[5] SW5_LED5 75
sw[4] SW4_LED4 74
sw[3] SW3_LED3 87
sw[2] SW2_LED2 86
sw[1] SW1_LED1 83
sw[0] SW0_LED0 80

The second step: observe the read and write results of the dual-port RAM with SignalTap

  1. In order to facilitate the observation of the read and write state machine synergy results, the data length is changed to 4 here and recompile.Users can test themselves using long data.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
module frame_ram
 
#(parameter data_len=4)
 
(
 
input inclk,
 
input rst,
 
input [6:0] sw,
 
output reg [6:0] oID,
 
output reg rd_done,
 
output reg rd_err
 
);

 

  1. Observe the simulation result
  2. Observe the handshake mechanism through dual-port RAM
  3. Determine whether the reading is started after the packet is written
  4. Determine whether the write packet is blocked before reading the entire packet is completed
  5. Observe the external interface signal and status
  6. rd_done, rd_err

Set rd_err = 1, or the rising edge is the trigger signal to observe whether the error signal is captured.

  1. Observe whether wren_a, wren_b signal and the state machine jump are strictly matched to meet the design requirements.
  2. SignalTap result. See Figure 9.1.

Figure 9.1 SignalTap II simulation

Experiment Summary and Reflection

  1. Review the design requirements of how to analyze an actual demand, to gradually establish a model of digital control and state machine and finally design it.
  2. Modify the third stage of the state machine into the if…else model and implement.
  3. Focus on thinking If the read and write clocks are different. After it becomes an asynchronous mechanism, how to control the handshake protocol.
  4. According to the above example, consider how dual-port RAM can be used in data acquisition, asynchronous communication, embedded CPU interface, and DSP chip interface.
  5. How to build ITCM with dual-port RAM and DTCM preparing for future CPU design.

 

————————————————————————————————-

old version (2019)

9.1 Experiment Objective

  1. Learn to configure and use dual-port RAM
  2. Learn to use synchronous clock to control the synchronization of frame structure
  3. Learn to use asynchronous clock to control the synchronization of frame structure
  4. Observing the synchronization structure of synchronous clock frames using SignalTap II
  5. Extended the use of dual-port RAM
  6. Design the use of three-stage state machine

9.2 Experiment Requirement

  1. Generate dual-port RAM and PLL

    1. 16-bit width, 256-depth dual-port RAM

    2. 2 PLL, both 50 MHz input, different 100 MHz and 20 MHz outputs

  1. Design a 16-bit data frame

    1. Data is generated by an 8-bit counter: Data={~counta,counta}

    2. The ID of the data frame inputted by the switch (7 bits express maximum of 128 different data frames)

    3. 16-bit checksum provides data verification

        1. 16-bit checksum accumulates, discarding the carry bit

        2. After the checksum is complemented, append to the frame data

   d. Provide configurable data length data_len by parameter
e. Packet: When the data and checksum package are written to the dual-port RAM, the userID, the frame length and the valid flag are written to the specific location of the dual-port RAM. The structure of the memory is shown in Table 9. 1

 

Wr_addr Date/ Flag Rd_addr
8’hff {valid, ID, data_len} 8’hff
N/A
8’hnn+2 N/A 8’hnn+2
8’hnn+1 ~checksum+1 8’hnn+1
8’hnn datann 8’hnn
….
8’h01 Data1 8’h01
8’h00 Data0 8’h00

 

Table 9. 1 Memory structure

  1. Read and write in an agreed order

Firstly, write in the order

  1. Read the flag of the 8’hff address (control word). If valid=1’b0, the program proceeds to the next step, otherwise waits

  2. Address plus 1, 8’hff+1 is exactly zero, write data from 0 address and calculate the checksum

  3. Determine whether the interpretation reaches the predetermined data length. If so, proceeds to next step, otherwise the data is written, and the checksum is calculated.

  4. checksum complements and write to memory

  5. Write the control word in the address 8’hff, packet it

Secondly, read in the order

  1. Idle is the state after reset

  2. Init: Initialization, set the address to 8’hff

  3. Rd_pipe0: Add a latency (since the read address and data are both latched). Address +1, forming a pipeline structure

  4. Read0: Set the address to 8’hff, read the control word and judge whether the valid bit is valid.

If valid=1’b1, address +1, proceeds to the next step

If valid=1’b0, it means the packet is not ready yet, the address is set to be 8’hff and returns to the init state.

  1. Read1: Read the control word again

If valid=1’b1, address+1, ID and data length are assigned to the corresponding variables and proceeds to the next step

If valid=1’b0, it means the packet is not ready yet, the address is set to 8’hff, and returns to the init state.

  1. Rd_data:

Read data and pass to data variables

Calculate checksum, data_len – 1

Determine whether the data_len is 0, if so, all data has been read, proceeds to the next step, otherwise, continue the operation in current state

  1. grd_chsum: Read the value of checksum and calculate the last checksum. Correct the data and set the flag of rd_err

  2. rd_done: The last step clears the valid flag in memory and opens the write enable for the next packet.

Thirdly, valid is the handshake signal. This flag provides the possibility of read and write synchronization, so the accuracy of this signal must be ensured in the program design. See the project files for more details.

9.3 Experiment

  1. Port
1
2
3
4
5
6
7
8
9
10
module frame_ram
#(parameter data_len=250)
(
input inclk,
input rst, //external reset
input [6:0]sw, //used as input ID
output reg[6:0] oID, //used as output ID
output reg rd_done, //frame read is done
output reg rd_err //frame read has errors
);
  1. Definition of state machine
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
parameter   [2:0] mema_idle=0,
                    mema_init=1,
                    mema_pipe0=2,
                    mema_read0=3,
                    mema_read1=4,
                        mema_wr_data=5,
                        mema_wr_chsum=6,
                        mema_wr_done=7;
 
    parameter   [2:0]   memb_idle=0,
                        memb_init=1,
                        memb_pipe0=2,
                        memb_read0=3,
                        memb_read1=4,
                        memb_rd_data=5,
                        memb_rd_chsum=6,
                        memb_rd_done=7;
  1. Define clock parameter
1
2
3
4
5
6
7
wire sys_clk;
 
wire BCD_clk;
 
wire sys_rst;
 
reg ext_clk;
  1. Define two-port RAM interface
1
2
3
4
5
6
7
8
9
10
11
12
13
reg [7:0] addr_a;
 
reg [15:0] data_a;
 
reg wren_a;
 
wire [15:0] q_a;
 
reg [7:0] addr_b;
 
reg wren_b;
 
wire [15:0] q_b;
  1. Write state machine partial variable definition
    1. Write state machine variables

reg[6:0] user_id;

reg[7:0] wr_len;

reg[15:0] wr_chsum;

wire wr_done;

reg[7:0] counta;

wire[7:0] countb=~counta;

reg ext_rst;

reg [2:0] sta;

reg[2:0] sta_nxt;

    1. Read state machine variables

reg[15:0] rd_chsum

reg[7:0] rd_len;

reg[15:0] rd_data;

reg ext_rst;

reg[2:0] stb;

reg[2:0] stb_nxt;

  1. Data generation counter

 

always@(posedge BCD_clk)

ext_rst<=rst;

always@(posedge sys_clk)

if(sys_rst) begin

counta <=0;

user_id <=0;

end

else begin

counta <=counta+1;

user_id<=sw;

end

  1. Write state machine
    1. First and second stages

 

assign wr_done=(wr_len==data_len-1); //Think why to use wr_len==data_len-1

//instead of wr_len==data_len

always@(posedge sys_clk)

if(sys_rst) begin

sta=mema_idle;

end

else

sta=sta_nxt;

always@(*)

case (sta)

mema_idle : sta_nxt=mema_init;

mema_init : sta_nxt=mema_pipe0;

mema_pipe0 : sta_nxt=mema_read0;

mema_read0 :begin

if(!q_a[15])

sta_nxt=mema_read1;

else

sta_nxt=sta;

end

mema_read1:begin

if(!q_a[15])

sta_nxt=mema_wr_data;

else

sta_nxt=sta;

end

mema_wr_data: begin

if(wr_done)

sta_nxt=mema_wr_chsum;

else

sta_nxt=sta;

end

mema_wr_chsum: sta_nxt=mema_wr_done;

mema_wr_done: sta_nxt=mema_init;

default:sta_nxt=mema_idle;

endcase

 

  1. Third stage

 

always@(posedge sys_clk)

case (sta)

mema_idle: begin

addr_a<=8’hff;

wren_a<=1’b0;

data_a<=16’b0;

wr_len<=8’b0;

wr_chsum<=0;

end

mema_init,mema_pipe0,mema_read0,mema_read1: begin

addr_a<=8’hff;

wren_a<=1’b0;

data_a<=16’b0;

wr_len<=8’b0;

wr_chsum<=0;

end

mema_wr_data:begin

addr_a<=addr_a+1;

wren_a<=1’b1;

data_a<={countb,counta};

wr_len<=wr_len+1;

wr_chsum<=wr_chsum+{countb,counta};

end

mema_wr_chsum:begin

addr_a<=addr_a+1;

wr_len<=wr_len+1;

wren_a<=1’b1;

data_a<=(~wr_chsum)+1’b1;

end

mema_wr_done:begin

addr_a<=8’hff;

wren_a<=1’b1;

data_a<={1’b1,user_id,wr_len};

end

default:;

endcase

  1. Read state machine
    1. First stage

always@(posedge sys_clk)

if(ext_rst) begin

stb=memb_idle;

end

else

stb=stb_nxt;

  1. Second stage

always @ (*)

case (stb)

memb_idle : stb_nxt=memb_init;

memb_init : stb_nxt=memb_pipe0;

memb_pipe0 : stb_nxt=memb_read0;

memb_read0 :begin

if(q_b[15])

stb_nxt=memb_read1;

else

stb_nxt=memb_init;

end

memb_read1:begin

if(q_b[15])

stb_nxt=memb_rd_data;

else

stb_nxt=memb_init;

end

memb_rd_data: begin

if(rd_done)

stb_nxt=memb_rd_chsum;

else

stb_nxt=stb;

end

memb_rd_chsum: stb_nxt=memb_rd_done;

memb_rd_done: stb_nxt=memb_init;

default:stb_nxt=memb_idle;

endcase

  1. Third stage. The actual operation is driven by the edge of the clock

always@(posedge sys_clk)

case(stb)

memb_idle: begin

addr_b<=8’hff;

rd_data<=0;

rd_chsum<=0;

wren_b<=1’b0;

rd_len<=8’b0;

oID<=7’b0;

rd_err<=1’b0;

end

memb_init: begin

addr_b<=8’hff;

rd_data<=0;

rd_chsum<=0;

wren_b<=1’b0;

rd_len<=8’b0;

oID<=7’b0;

rd_err<=1’b0;

endmemb_pipe0: begin

addr_b<=8’b0;

end

memb_read0: begin

if(q_b[15])

addr_b<=addr_b+1’b1;

else

addr_b<=8’hff;

rd_data<=0;

rd_chsum<=0;

wren_b<=1’b0;

rd_len<=8’b0;

oID<=7’b0;

end

memb_read1: begin

if(q_b[15])

addr_b<=addr_b+1’b1;

else

addr_b<=8’hff;

rd_data<=0;

rd_chsum<=0;

wren_b<=1’b0;

rd_len<=q_b[7:0];

oID<=q_b[14:8];

end

memb_rd_data: begin

addr_b<=addr_b+1’b1;

rd_data<=q_b;

rd_chsum<=rd_chsum+rd_data;

wren_b<=1’b0;

rd_len<=rd_len-1’b1;

end

memb_rd_chsum: begin

addr_b<=8’hff;

wren_b<=1’b0;

if(|rd_chsum)//Determine if rd_chsum is not 0, error occurs when reading data

rd_err<=1’b1;

end

memb_rd_done: begin

addr_b<=8’hff;

wren_b<=1’b1;

end

default:;

endcase

always@(*)begin

if(stb==memb_rd_data)

rd_done=(rd_len==0);

else

rd_done=1’b0;

end

  1. Instantiate dual-port RAM and PLL

//Instantiate dual-port RAM

dp_ram dp_ram_inst

(

.address_a(addr_a),

.address_b(addr_b),

.clock (sys_clk),

.data_a (data_a),

.data_b (16’b0),

.wren_a (wren_a),

.wren_b(wren_b),

.q_a (q_a),

.q_b (q_b)

);

//Instantiate PLL

pll_sys_rst pll_sys_rst_inst

(

.inclk (inclk),

.sys_clk (sys_clk),

.BCD_clk(BCD_clk),

.sys_rst (sys_rst)

);

endmodule

9.4 Lock the Pins, Compile, and Download to FII-PRA006 FPGA to Test

Signal Name

Port Description

Network Label

FPGA Pin

Inclk

Input clock

C10_50MCLK

91

rst

Reset signal

KEY2

10

sw[6]

Switch input 6

SW6_LED6

76

sw[5]

Switch input 5

SW5_LED5

75

sw[4]

Switch input 4

SW4_LED4

74

sw[3]

Switch input 3

SW3_LED3

87

sw[2]

Switch input 2

SW2_LED2

86

sw[1]

Switch input 1

SW1_LED1

83

sw[0]

Switch input 0

SW0_LED0

80

9.5 Use SignalTap II to Observe the Dual-port RAM Read and Write

        1. In order to facilitate the observation of the read and write state machine synergy results, the data length is changed to 4 here, recompile and download. Users can test themselves using long data.

module frame_ram

#(parameter data_len=4)

(

input inclk,

input rst, //external reset

input [6:0]sw, //used as input ID

output reg[6:0] oID, //used as output ID

output reg rd_done, //frame read is done

output reg rd_err //frame read has errors

)

        1. SignalTap II simulation result. See Fig 9. 1

          singal Tap
          singal Tap

Fig 9. 1 SingalTap II simulation

        1. Observe the simulation result

  1. Observe the handshake mechanism through dual-port RAM

Determine whether the reading is started after the packet is written, whether the write packet is blocked before reading the entire packet is completed.

  1. Observe the external interface signal and status

Rd_done, rd_err

Set rd_err = 1, or the rising edge is the trigger signal to observe whether the error signal is captured.

Observe whether wren_a, wren_b signal and the state machine jump are strictly matched to meet the design requirements.

9.6 Experiment Summary and Reflection

              1. Review the design requirements. How to analyze an actual demand, gradually establish a model of digital control and state machine and finally design.

              2. Modify the third stage of the state machine into the if…else model and implement.

              3. Focus on thinking If the read and write clocks are different, it becomes an asynchronous mechanism, how to control the handshake.

              4. According to the above example, consider how dual-port RAM can be used in data acquisition, asynchronous communication, embedded CPU interface, and DSP chip interface.

              5. How to build ITCM with dual-port RAM and DTCM preparing for future CPU design.

Visited 1 times, 1 visit(s) today

Related posts